#include"Date.h"
using namespace std;

//member function definitions
Date::Date(int yy, Month mm, int dd) : y(yy),m(mm),d(dd) {
  if(!is_date(yy, mm, dd)) throw Invalid();
}

const Date& default_date() {
  static Date dd(2001, Date::jan, 1);
  return dd;
}

Date::Date() : y(default_date().year()), m(default_date().month()), d(default_date().day()) {
}


/* Not yet needed, probably doesn't work
void Date::add_day(int n) {	//RCD A.2
  while(n > 0) {	//repeat loop until number of days has been added 
    while(is_date(y, m, d) && n > 0) {	//add days until days overflows, making Date no longer valid
      d++;
      n--;
    }
    inc_Month();	//fix day overflow, add month
    d = 1;	//fix day overflow, wrap around to 1
    if(m > 12) {	//if month overflows, making Date invalid
      y++;	//fix month overflow, add year
      m = jan;	//fix month overflow, wrap around to january
    }
  }
}

void Date::add_month(int n) {	//RCD A.2
  while(n > 0) {	//repeat loop until number of months has been added
    while(is_date(y, m, d) && n > 0) {	//repeat loop until date is no longer valid because months have overflow
      inc_Month();
      n--;
    }
    y++;	//fix month overflow, add year
    m = jan;	//fix month overflow, wrap around to january
  }
}
*/


void Date::add_year(int n) {
  if(m==feb && d==29 && !leapyear(y+n)) {
    m = mar;	//use March 1 instead of February 29
    d = 1;
  }
  y += n;
}


/*	Not yet needed, probably doesn't work
void Date::inc_Month() {	//RCD A.2  increment month by one
  switch(m) {
  case jan:
    m = feb;
    break;
  case feb:
    m = mar;
    break;
  case mar:
    m = apr;
    break;
  case apr:
    m = may;
    break;
  case may:
    m = jun;
    break;
  case jun:
    m = jul;
    break;
  case jul:
    m = aug;
    break;
  case aug:
    m = sep;
    break;
  case sep:
    m = oct;
    break;
  case oct:
    m = nov;
    break;
  case nov:
    m = dec;
    break;
  case dec:
    m = jan;
    y++;
    break;
  }
}
*/


//helper functions
bool is_date(int y, Date::Month m, int d) {
  //assume y is valid
  if(d <= 0)
    return false;	//d must be positive
  if(m < Date::jan || m > Date::dec)
    return false;

  int days_in_month = 31;	//most months

  switch(m) {
  case Date::feb : days_in_month = (leapyear(y)) ? 29 : 28;
    break;
  case Date::apr : case Date::jun : case Date::sep : case Date::nov : days_in_month = 30;
    break;
      }

  if (days_in_month < d)
    return false;

  return true;
}

bool leapyear(int y) {	//RCD A.2
  if(y%4 == 0) {
    if(y%100 == 0) {
      if(y%400 == 0) {
	return true;
      }
      return false;
    }
    return true;
  }
  return false;
}

bool operator==(const Date& a, const Date& b) {
  return a.year() == b.year() && a.month() == b.month() && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b) {
  return !(a == b);
}

ostream& operator<<(ostream& os, const Date& d) {	//RCD A.2 modified to output with spaces instead of commas or parentheses
  return os << d.year() << ' ' << d.month() << ' ' << d.day();
}

istream& operator>>(istream& is, Date& dd) {	//RCD A.2 modified to take input with spaces instead of commas or parenthses
  int y, m, d;
  is >> y >> m >> d;
  if(!is)
    return is;
  dd = Date(y, Date::Month(m), d);
  return is;
}


/*
enum Day {sunday, monday, tuesday, wednesday, thursday, friday, saturday};

Day day_of_week(const Date& d) {
  //...
}

Date next_Sunday(const Date& d) {
  //...
}

Date next_weekday(const Date& d) {
  //...
}
*/
